package com.apes.fn.scm.internal.help.service.voucher.retail;

import com.apes.fn.scm.retailOrder.model.RetailOrder;
import com.apes.fn.scm.retailReturn.RetailReturn;
import com.apes.fn.scm.retailReturn.RetailReturnItem;
import com.apes.framework.api.DomainService;
import com.apes.framework.api.SpringManager;
import com.apes.framework.jpa.spec.JpaDsl;
import com.apes.framework.util.MapUtil;
import com.apes.framework.util.ReflectUtil;
import com.apes.scm.account.account.model.AccountingMethod;
import com.apes.scm.account.account.model.InventoryBatch;
import com.apes.scm.account.account.model.InventoryMode;
import com.apes.scm.account.account.service.InventoryService;
import com.apes.scm.contract.model.Contract;
import com.apes.scm.contract.service.ContractService;
import com.apes.scm.masterdata.frame.model.Dept;
import com.apes.scm.masterdata.frame.model.DeptEnum;
import com.apes.scm.masterdata.frame.repository.DeptRepository;
import com.apes.scm.masterdata.party.model.PartyRole;
import com.apes.scm.masterdata.party.repository.PartyRoleRepository;
import com.apes.scm.voucher.model.Voucher;
import com.apes.scm.voucher.model.VoucherInventory;
import com.apes.scm.voucher.model.VoucherItem;
import com.apes.scm.voucher.repository.VoucherInventoryRepository;
import com.apes.scm.voucher.repository.VoucherRepository;
import com.apes.scm.voucher.service.BusinessType;
import com.apes.scm.voucher.service.VoucherType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * 年审退回
 *
 * @Author wuq
 * @Date 2022-1-18
 */
@Service
public class AnnualAuditReturnVoucherService extends DomainService {

    @Autowired
    private VoucherInventoryRepository voucherInventoryRepository;
    @Autowired
    private VoucherRepository voucherRepository;
    @Autowired
    private ContractService contractService;
    @Autowired
    private InventoryService inventoryService;
    @Autowired
    private PartyRoleRepository partyRoleRepository;

    public void process(RetailReturn retailReturn) {

        Map<String, List<RetailReturnItem>> outRetailReturnItems = retailReturn.getSaleItems().stream()
                .collect(Collectors.groupingBy(returnItem ->
                        returnItem.getRetailOrderItem().getOriginSupplier().getId() + ";" + returnItem.getRetailOrderItem().getPlatform().getId()
                ));

        outRetailReturnItems.forEach((unique, retailReturnItems) -> {
            String[] uniques = unique.split(";");
            String platformId = uniques[1];    // 平台公司
            // 取原始供应商，由于零售回写批次的时候，会修改供应商字段
            PartyRole supplier = retailReturnItems.get(0).getRetailOrderItem().getOriginSupplier().getParty().getPartyRoleByType("Supplier");

            if ( "1001".equals(platformId) && platformId.equals(retailReturn.getCompany().getId()) ) {
                // 1001 平台， 直接找供应商采购(直营门店)，1001 对供应商的采购合同，直接退 1001 对供应商的采购
                Contract contract = contractService.getContract(retailReturn.getDept(), supplier, "consignment", "supplier", "零售单年审外协");
                returnSupplierPurchase(retailReturn, retailReturnItems, supplier, contract);
            }

            if ( "1001".equals(platformId) && !platformId.equals(retailReturn.getCompany().getId()) ) {
                // 1001 平台， 找 1001 做采购（佛山车宝，志运门店），1001 对供应商的采购合同

                // 跨公司采购
                PartyRole platformCompany = partyRoleRepository.findOne(platformId);
                Contract contract = contractService.getContract(getDept(platformCompany), supplier, "consignment", "supplier", "零售单年审外协");
                returnCrossPurchaseVoucher(retailReturn, retailReturnItems, contract);
            }

            if ( "1007".equals(platformId) ) {
                // 1007 平台， 直接退供应商采购(经销采购)
                List<Contract> contracts = contractService.getContract(VoucherType.PLATFORM_CODE, supplier.getId());
                Contract contract = contracts.stream()
                        .filter(c -> "platform".equals(c.getContractType().getCode()))
                        .findFirst()
                        .orElseThrow(()-> new RuntimeException(String.format("没有找到供应商 [ %s ] ，对应的[ %s ] 平台合同", supplier.getId(), VoucherType.PLATFORM_CODE)));

                returnSupplierPurchase(retailReturn, retailReturnItems, supplier, contract);
            }
        });
    }

    private Dept getDept(PartyRole company) {
        return SpringManager.getBean(DeptRepository.class)
                .findAll(JpaDsl.toCriteriaByEq("company", company, "innerTag", true, "deptType.id", "000006"))
                .stream().findFirst().orElseThrow(() -> new RuntimeException("没有找到内定标记部门，请先定义！公司：：" + company.getId()));
    }

    private void returnSupplierPurchase(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems,
                                        PartyRole supplier, Contract contract){
        // 找原销售凭证
        Voucher originSaleVoucher = voucherRepository.findOne(
                JpaDsl.toCriteriaByEq("sourceBusinessType", RetailOrder.class.getSimpleName(),
                        "source", retailReturn.getSaleOrder().getId(), "origin", retailReturn.getSaleOrder().getId(), "voucherType", "CUSTOMER_SALE"))
                .orElseThrow(() -> new RuntimeException("生成年审代销带安返厂凭证时，未找到：" + retailReturn.getSaleOrder().getId() + " 的业务凭证！"));

        returnOutPurchaseVoucher(contract, contract.getPartner(), originSaleVoucher, retailReturn, retailReturnItems);
    }

    private void returnOutPurchaseVoucher(Contract contract, PartyRole partner, Voucher originSaleVoucher,
                                          RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems) {
        Voucher voucher = new Voucher();
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.PURCHASE_RETURN);

        voucher.setPartner(partner.getParty().getPartyRoleByType("Supplier"));
        voucher.setCompany(retailReturn.getCompany());
        voucher.setDept(retailReturn.getDept());
        voucher.setClerk(retailReturn.getClerk());

        voucher.setContract(contract);
        voucher.setInventoryMode(contract.getCooperation().getInventoryMode());
        voucher.setAccountingMethod(contract.getCooperation().getAccountingMethod());

        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());
        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());

        voucher.setVoucherItems(
                retailReturnItems.stream()
                        .map(retailReturnItem -> {
                            VoucherItem voucherItem = new VoucherItem();

                            voucherItem.setProduct(retailReturnItem.getProduct());
                            voucherItem.setUom(retailReturnItem.getUom());
                            voucherItem.setProductQty(-1.00 * retailReturnItem.getProductUomQty());
                            voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                            voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));

                            //找到原销售凭证的明细行
                            VoucherItem saleVoucherItem = originSaleVoucher.getVoucherItems().stream()
                                    .filter(saleItem -> saleItem.getProduct().getId().equals(retailReturnItem.getProduct().getId()))
                                    .findFirst()
                                    .orElseThrow(() -> new RuntimeException("生成返厂凭证时，根据商品编码没有找到对应的凭证明细，" +
                                            "商品编码：" + retailReturnItem.getProduct().getId() + "，凭证编码：" + originSaleVoucher.getId()));

                            // 获取原销售批次
                            VoucherInventory voucherInventory = voucherInventoryRepository.findAll(
                                    JpaDsl.toCriteriaByEq("voucher.id", originSaleVoucher.getId(), "voucherItem", saleVoucherItem))
                                    .stream().findFirst().get();
                            InventoryBatch inventoryBatch = voucherInventory.getInventoryBatch();

                            // 由于存在变价，这里按照 SaleReturnCostMethod 149 行的处理逻辑处理, 没有考虑多个批次的情况
//                            List<InventoryBatch> inventoryBatchs = inventoryService.findAllByBatchNumber(Arrays.asList(voucherInventory.getInventoryBatch().getBatchNumber()), inventory -> inventory.getBalanceQty());
//                            inventoryBatch = inventoryBatchs.get(0);

                            voucherItem.setInventoryBatch(inventoryBatch);
                            voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                            voucherItem.setSupplier(inventoryBatch.getSupplier());
                            voucherItem.setTax(inventoryBatch.getTax());
                            voucherItem.setOwner(retailReturn.getCompany());

                            //  增加（减少）冻结数量
                            inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                                    retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成零售外协商品返厂凭证");

                            return voucherItem;
                        }).collect(Collectors.toSet()));

        Voucher newVoucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(newVoucher.getInventoryMode()) && newVoucher.getAmtTotal() != 0) {
            invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(newVoucher))));
        }
    }

    private void returnCrossPurchaseVoucher(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, Contract contract){
        PartyRole company = contract.getCompany().getParty().getPartyRoleByType("Company");         // 合同上面的公司
        PartyRole partner = retailReturn.getCompany().getParty().getPartyRoleByType("Customer");    // 门店公司

        // 第一步：门店对合同公司做返厂
        Voucher storeOriginSaleVoucher = voucherRepository.findOne(
                JpaDsl.toCriteriaByEq("sourceBusinessType", RetailOrder.class.getSimpleName(),
                        "source", retailReturn.getSaleOrder().getId(), "origin", retailReturn.getSaleOrder().getId(), "voucherType", "CUSTOMER_SALE"))
                .orElseThrow(() -> new RuntimeException("生成年审代销带安返厂凭证时，未找到：" + retailReturn.getSaleOrder().getId() + " 的业务凭证！"));
        // 获取合同（内部修改为经销合同）
        Contract innerContract = contractService.getContract(retailReturn.getDept(), company.getParty().getPatyRoleByType("Supplier"), "distribution", "supplier", "零售年审带安业务");
        returnOutPurchaseVoucher(innerContract, company, storeOriginSaleVoucher, retailReturn, retailReturnItems);

        // 第二步：合同公司退对门店的销售
        // 找原内部销售凭证
        String originNumber = retailReturnItems.get(0).getRetailOrderItem().getId();                          // 由于外协部分是在一张凭证上面，这里就取其中的一条
        Voucher originSaleVoucher = voucherRepository.findSaleVoucherByCompanyAndItemOrigin(company, partner, originNumber);
        if (originSaleVoucher == null) throw new RuntimeException("没有找到对应的销售凭证，请检查！");
        returnSaleVoucher(retailReturn, retailReturnItems, originSaleVoucher);

        // 第三步：退对供应商的采购
        returnSupplierPurchaseVoucher(contract, contract.getPartner(), retailReturn, retailReturnItems);
    }

    /**
     * 构建销售退回凭证
     *
     * @param retailReturn      零售退货主表
     * @param retailReturnItems 零售退货明细
     * @param originSaleVoucher 原销售凭证
     */
    private void returnSaleVoucher(RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems, Voucher originSaleVoucher) {
        Voucher voucher = new Voucher();
        // 销售退回必填字段
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.SALE_RETURN);
        voucher.setVoucher(originSaleVoucher);

        ReflectUtil.copy(originSaleVoucher, voucher, "dept", "company", "partner", "contract");
        voucher.setInventoryMode(originSaleVoucher.getInventoryMode());     //设置库存模式
        voucher.setAccountingMethod(originSaleVoucher.getAccountingMethod());  // 核算方式

        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());
        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());

        voucher.setVoucherItems(
                originSaleVoucher.getVoucherItems().stream().map(saleItem -> {
                    VoucherItem voucherItem = new VoucherItem();

                    retailReturnItems.stream()
                            .filter(returnItem -> returnItem.getProduct().getId().equals(saleItem.getProduct().getId()))
                            .forEach(retailReturnItem -> {
                                voucherItem.setOwner(originSaleVoucher.getCompany());
                                voucherItem.setProduct(saleItem.getProduct());
                                voucherItem.setTax(saleItem.getTax());

                                voucherItem.setUom(saleItem.getUom());          // 设置为销售的单位以及数量
                                voucherItem.setProductQty(-1 * Math.abs(saleItem.getProductQty()));
                                voucherItem.setPriceUnit(saleItem.getPriceUnit());      // 默认为退货单价

                                voucherItem.setOriginItemId(retailReturnItem.getId());
                                voucherItem.setSourceItemId(retailReturnItem.getId());
                                voucherItem.setVoucherItem(saleItem);   // 退回时传入明细行号
                            });

                    return voucherItem;
                }).filter(voucherItem -> voucherItem.getProduct() != null).collect(Collectors.toSet()));

        voucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(voucher.getInventoryMode()) && voucher.getAmtTotal() != 0) {
            invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(voucher))));
        }
    }

    /**
     * 退外部供应商
     * 跨公司外协时，找供应商采购时
     *
     * @param contract          合同
     * @param partner           合作伙伴
     * @param retailReturn      零售退货单
     * @param retailReturnItems 零售退货明细
     */
    private void returnSupplierPurchaseVoucher(Contract contract, PartyRole partner,
                                               RetailReturn retailReturn, List<RetailReturnItem> retailReturnItems) {
        Voucher voucher = new Voucher();
        voucher.setReturned(true);
        voucher.setBusinessType(BusinessType.PURCHASE_RETURN);

        voucher.setPartner(partner.getParty().getPartyRoleByType("Supplier"));
        voucher.setCompany(contract.getCompany());
        voucher.setDept(contract.getDept());
        voucher.setClerk(retailReturn.getClerk());

        voucher.setContract(contract);

        voucher.setInventoryMode(contract.getCooperation().getInventoryMode());
//        voucher.setInventoryMode(InventoryMode.IM_OWN);             //设置库存模式：自有
        voucher.setAccountingMethod(AccountingMethod.AM_PURCHASE);  // 核算方式：经销

        voucher.setScene(retailReturn.getScene());
        voucher.setOrigin(retailReturn.getId());
        voucher.setOriginBusinessType(retailReturn.getInnerBusinessType());
        voucher.setSource(retailReturn.getId());
        voucher.setSourceBusinessType(retailReturn.getInnerBusinessType());
        voucher.setCustomerExpand(retailReturn.getCustomerExpand().getId());

        Dept dept = voucher.getDept();
        if (dept == null) {
            throw new RuntimeException("业务凭证销售部门为空！");
        }
        if (!(dept.existDeptType(DeptEnum.RETAIL) || dept.existDeptType(DeptEnum.SALE))) {
            throw new RuntimeException("业务凭证部门为非销售部门！");
        }
        if (!dept.existDeptType(DeptEnum.PURCHASE)) {
            throw new RuntimeException("业务凭证部门为非采购部门！");
        }

        Set<VoucherItem> voucherItems = new HashSet(); //凭证明细
        // 明细处理
        for (int i = 0, len = retailReturnItems.size(); i < len; i++) {
            RetailReturnItem retailReturnItem = retailReturnItems.get(i);

            // 查找对应的批次数据
            List<InventoryBatch> inventoryBatches = inventoryService.findInventoryBatchForSaleOrder(voucher.getCompany(), dept, voucher.getPartner(), retailReturnItem.getProduct());
            if (inventoryBatches.isEmpty()) throw new RuntimeException("代销带安退货时，没有找到对应的批次数据");
            double countBatchNumber = retailReturnItem.getProductUomQty();   // 计数器
            // 循环与批次数据作比较，根据批次拆分
            for (InventoryBatch inventoryBatch : inventoryBatches) {

                if (inventoryBatch.getBalanceQty() >= countBatchNumber) {    // 批次库存大于退货数量
                    VoucherItem voucherItem = new VoucherItem();
                    voucherItem.setProduct(retailReturnItem.getProduct());

                    voucherItem.setUom(retailReturnItem.getUom());
                    voucherItem.setProductQty(-1.00 * countBatchNumber);
                    voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setInventoryBatch(inventoryBatch);
                    voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    voucherItem.setSupplier(inventoryBatch.getSupplier());
                    voucherItem.setTax(inventoryBatch.getTax());
                    voucherItem.setOwner(contract.getCompany());

                    //  增加（减少）冻结数量
                    inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                            retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成跨公司零售外协商品返厂凭证");
                    voucherItems.add(voucherItem);
                    break;
                } else {    // 批次库存小于退货数量时
                    VoucherItem voucherItem = new VoucherItem();
                    voucherItem.setProduct(retailReturnItem.getProduct());

                    voucherItem.setUom(retailReturnItem.getUom());
                    voucherItem.setProductQty(-1.00 * inventoryBatch.getBalanceQty());
                    voucherItem.setOriginItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setSourceItemId(String.valueOf(retailReturnItem.getId()));
                    voucherItem.setInventoryBatch(inventoryBatch);
                    voucherItem.setPriceUnit(inventoryBatch.getTaxCostPrice());
                    voucherItem.setSupplier(inventoryBatch.getSupplier());
                    voucherItem.setTax(inventoryBatch.getTax());
                    voucherItem.setOwner(contract.getCompany());

                    //  增加（减少）冻结数量
                    inventoryService.changeFrostQty(inventoryBatch, retailReturnItem.getUom(), retailReturnItem.getProductUomQty(), inventoryBatch.getCompany(),
                            retailReturn.getInnerBusinessType(), retailReturn.getId(), retailReturnItem.getId(), "生成跨公司零售外协商品返厂凭证");

                    countBatchNumber -= inventoryBatch.getBalanceQty();     // 计数操作
                    voucherItems.add(voucherItem);
                }
            }
        }
        voucher.setVoucherItems(voucherItems);

        Voucher newVoucher = invoke("voucher.create", MapUtil.mapped(voucher));
        if ("IM_OWN".equals(newVoucher.getInventoryMode()) && voucher.getAmtTotal() != 0) {
            invoke("invoice.create", MapUtil.mapped(invoke("voucher.flow.invoice", MapUtil.mapped(newVoucher))));
        }
    }
}
